package com.feng.yupaoback.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.feng.yupaoback.common.ErrorCode;
import com.feng.yupaoback.constant.BaseConst;
import com.feng.yupaoback.exception.BusinessException;
import com.feng.yupaoback.pojo.dto.UserUpdateInfoRequest;
import com.feng.yupaoback.pojo.entity.User;
import com.feng.yupaoback.service.UserService;
import com.feng.yupaoback.mapper.UserMapper;
import com.feng.yupaoback.utils.AlgorithmUtils;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.math3.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
* @author 34689
* @description 针对表【user_yu_pao(鱼泡用户)】的数据库操作Service实现
* @createDate 2025-07-03 14:45:35
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{

    @Resource
    UserMapper userMapper;

    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

    /**
     * 用于加密
     */
    private static final String SALT = "liquor";

    /**
     * @param userAccount   账户
     * @param userPassword  密码
     * @return 用户信息（脱敏）
     */
    @Override
    public User userLogin(String userAccount, String userPassword, HttpServletRequest request) {

        // 1. 校验
        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "账户或密码为空！");
        }
        if (userAccount.length() < 4 || userPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账户或密码格式不符合要求！");
        }

        // 不能包含特殊字符
        String validPattern = "[\\p{Punct}\\p{S}\\s]"; // 标点、符号、空白
        Matcher matcher = Pattern.compile(validPattern).matcher(userAccount);

        if (matcher.find()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账户包含特殊字符！");
        }

        // 2. 密码加密转换
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());

        // 查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        queryWrapper.eq("userPassword", encryptPassword);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            log.info("用户登录失败");
            throw new BusinessException(ErrorCode.NULL_ERROR, "不存在该用户！");
        }

        // 3. 用户信息脱敏
        User safeUser = getSafetyUser(user);

        // 4. 记录用户登录态
        request.getSession().setAttribute(BaseConst.USER_LOGIN_STATUS, safeUser);
        return safeUser;
    }

    /**
     * @param userAccount   账户
     * @param userPassword  密码
     * @param checkPassword 校验码
     * @param planetCode 星球编号
     * @return 注册
     */
    @Override
    public long userRegister(String userAccount, String userPassword, String checkPassword, String planetCode) {

        // 校验
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword, planetCode)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "参数为空！");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短！");
        }
        if (userPassword.length() < 8 ) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短！");
        }
        if (!checkPassword.equals(userPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次密码不一致！");
        }
        if (planetCode.length() > 4){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "星球编号过长！");
        }

        // 用户账号不能包含特殊字符
        String validPattern = "[\\p{Punct}\\p{S}\\s]"; // 标点、符号、空白
        Matcher matcher = Pattern.compile(validPattern).matcher(userAccount);

        if (matcher.find()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号包括非法字符！");
        }


        // 账号不能重复
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        long count = userMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "该账户已存在！");
        }

        // 星球编号不能重复
        queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("planetCode", planetCode);
        count = userMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "该星球编号已存在！");
        }

        // 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());

        // 插入数据
        User user = new User();
        user.setUserAccount(userAccount);
        user.setUserPassword(encryptPassword);
        user.setPlanetCode(planetCode);
        boolean flag = this.save(user);
        if (!flag) {
            throw new RuntimeException("系统错误！");
        }

        return user.getId();
    }

    @Override
    public User getCurrentUser(HttpServletRequest request) {
        User loginUser = (User) request.getSession().getAttribute(BaseConst.USER_LOGIN_STATUS);
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN, "未登录");
        }
        User user = getSafetyUser(this.getById(loginUser.getId()));
        if (user == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "不存在该用户！");
        }
        // 每一次查询当前登录用户更新一次登录态，防止信息改变不及时 (优化，可以放定时任务)
        request.getSession().setAttribute(BaseConst.USER_LOGIN_STATUS, user);
        return user;
    }

    /**
     * 修改密码
     * @param oldPassword 旧密码
     * @param newPassword 新密码
     * @param checkPassword 校验密码
     * @param request 请求
     * @return 执行结果
     */
    @Override
    public long updatePassword(String oldPassword, String newPassword, String checkPassword, HttpServletRequest request) {
        User satetyUser = (User)request.getSession().getAttribute(BaseConst.USER_LOGIN_STATUS);
        if (StringUtils.isAnyBlank(oldPassword, checkPassword, newPassword) || satetyUser == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "参数为空！");
        }

        // 根据当前脱敏信息取得密码
        int userId = satetyUser.getId();
        User user = getById(userId);
        String userPwd = user.getUserPassword();

        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + oldPassword).getBytes());
        if (!newPassword.equals(checkPassword) || !encryptPassword.equals(userPwd)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码输入错误！");
        }

        // 修改密码
        user.setId(userId);
        String newEncryptPassword = DigestUtils.md5DigestAsHex((SALT + newPassword).getBytes());
        user.setUserPassword(newEncryptPassword);

        return userMapper.updateById(user);
    }

    /**
     * 修改个人信息
     * @param userUpdateInfoRequest 修改信息体
     * @return 修改后的信息
     */
    @Override
    public User updateInfo(UserUpdateInfoRequest userUpdateInfoRequest, HttpServletRequest request) {
        if (userUpdateInfoRequest == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "参数为空！");
        }
        User originUser = (User)request.getSession().getAttribute(BaseConst.USER_LOGIN_STATUS);
        if (originUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN, "当前未登录！");
        }

        // 设置需要更新的字段（只设置非空字段）
        if (StringUtils.isNotBlank(userUpdateInfoRequest.getUsername())) {
            originUser.setUsername(userUpdateInfoRequest.getUsername());
        }
        if (StringUtils.isNotBlank(userUpdateInfoRequest.getEmail())) {
            originUser.setEmail(userUpdateInfoRequest.getEmail());
        }
        if (StringUtils.isNotBlank(userUpdateInfoRequest.getUserAccount())) {
            originUser.setUserAccount(userUpdateInfoRequest.getUserAccount());
        }
        if (StringUtils.isNotBlank(userUpdateInfoRequest.getPhone())) {
            originUser.setPhone(userUpdateInfoRequest.getPhone());
        }
        if (StringUtils.isNotBlank(userUpdateInfoRequest.getAvatarUrl())) {
            originUser.setAvatarUrl(userUpdateInfoRequest.getAvatarUrl());
        }
        if (StringUtils.isNotBlank(userUpdateInfoRequest.getPlanetCode())) {
            originUser.setPlanetCode(userUpdateInfoRequest.getPlanetCode());
        }
        originUser.setGender(userUpdateInfoRequest.getGender());
        originUser.setUpdateTime(new Date());

        // 执行更新
        boolean flag = updateById(originUser);

        // 更新登录态
        if (!flag){
            throw new RuntimeException("系统错误！");
        }
        User safetyUser = getSafetyUser(originUser);
        request.getSession().setAttribute(BaseConst.USER_LOGIN_STATUS, safetyUser);
        return safetyUser;

    }

    /**
     * @param username 用户昵称
     * @return 用户列表
     */
    @Override
    public List<User> searchUsers(String username) {

        if (StringUtils.isNotBlank(username)) {
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.like("username", username);
            return userMapper.selectList(queryWrapper);
        }
        return userMapper.selectList(null);
    }

    /**
     * 用户注销
     * @param request 请求
     * @return 默认
     */
    @Override
    public int userLogout(HttpServletRequest request) {
        request.getSession().removeAttribute(BaseConst.USER_LOGIN_STATUS);
        return 1;
    }

    /**
     * 用户脱敏
     * @param originalUser 原始数据
     * @return 脱敏数据
     */
    @Override
    public User getSafetyUser(User originalUser) {
        if (originalUser == null){
            throw new BusinessException(ErrorCode.NULL_ERROR, "用户参数为空！");
        }
        User safeUser = new User();
        safeUser.setId(originalUser.getId());
        safeUser.setUsername(originalUser.getUsername());
        safeUser.setUserAccount(originalUser.getUserAccount());
        safeUser.setAvatarUrl(originalUser.getAvatarUrl());
        safeUser.setGender(originalUser.getGender());
        safeUser.setProfile(originalUser.getProfile());
        safeUser.setPhone(originalUser.getPhone());
        safeUser.setEmail(originalUser.getEmail());
        safeUser.setUserStatus(originalUser.getUserStatus());
        safeUser.setCreateTime(originalUser.getCreateTime());
        safeUser.setUpdateTime(originalUser.getUpdateTime());
        safeUser.setUserRole(originalUser.getUserRole() );
        safeUser.setPlanetCode(originalUser.getPlanetCode());
        safeUser.setTags(originalUser.getTags());
        return safeUser;
    }

    /**
     * 推荐用户
     * @param pageSize 页面大小
     * @param pageNum 页数
     * @return 返回分页用户
     */
    @Override
    public Page<User> getRecommendUsers(long pageSize, long pageNum){
        // 走数据库
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        Page<User> userList = this.page(new Page<>(pageNum, pageSize), queryWrapper);
        userList.setRecords(userList.getRecords().stream().map(this::getSafetyUser).collect(Collectors.toList()));

        return userList;
    }

    /**
     * 根据标签来搜索用户 内存版本
     * @param tagList 查询的标签
     * @return 符合的用户
     */
    @Override
    public List<User> searchUsersByTags(List<String> tagList) {
        if (CollectionUtils.isEmpty(tagList)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数请求错误");
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        List<User> userList = userMapper.selectList(queryWrapper);
        Gson gson = new Gson();
        // 在内存中判断是否包含指定标签
        return userList.stream().filter(user -> {
            String tagsStr = user.getTags();
            Set<String> tagNameList  = gson.fromJson(tagsStr, new TypeToken<Set<String>>() {}.getType());
            tagNameList = Optional.ofNullable(tagNameList).orElse(new HashSet<>());
            for (String tagName : tagList){
                if (!tagNameList.contains(tagName)){
                    return false;
                }
            }
            return true;
        }).map(this::getSafetyUser).collect(Collectors.toList());
    }

    /**
     * 根据标签来搜索用户 Sql版本，暂时不使用
     * @param tagList 查询的标签
     * @return 符合的用户
     */
    @Deprecated
    private List<User> searchUsersByTagsBySql(List<String> tagList){
        if (CollectionUtils.isEmpty(tagList)){
            log.info("未选择标签！");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数请求错误");

        }
        // 方法1 ：Sql搜索查询
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        // 拼接查询
        // like '%java%' and like '%Python%'
        for (String tagName : tagList){
            queryWrapper = queryWrapper.like("tags", tagName);
        }
        List<User> userList = userMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(userList)){
            return userList;
        }
        return userList.stream().map(this::getSafetyUser).collect(Collectors.toList());

        // 方法二 ：在内存里查询
        // 先查询所有的用户
//        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//        List<User> userList = userMapper.selectList(queryWrapper);
//        Gson gson = new Gson();
//        // 在内存中判断是否包含指定标签
//        return userList.stream().filter(user -> {
//            String tagsStr = user.getTags();
//            Set<String> tagNameList  = gson.fromJson(tagsStr, new TypeToken<Set<String>>() {}.getType());
//            for (String tagName : tagList){
//                if (!tagNameList.contains(tagName)){
//                    return false;
//                }
//            }
//            return true;
//        }).map(this::getSafetyUser).collect(Collectors.toList());
    }

    /**
     * 是否为管理员
     * @param request 请求
     * @return 管理员
     */
    @Override
    public boolean isAdmin(HttpServletRequest request){
        return this.getCurrentUser(request).getUserRole() == BaseConst.ADMIN_USER;
    }

    /**
     * 是否为管理员
     * @param loginUser 登录用户
     * @return 管理员
     */
    @Override
    public boolean isAdmin(User loginUser){
        if (loginUser == null){
            throw new BusinessException(ErrorCode.NULL_ERROR, "登录用户为空！");
        }
        return loginUser.getUserRole() == BaseConst.ADMIN_USER;
    }

    /**
     * 获取匹配用户
     * @param num 数量
     * @param loginUser 登录用户
     * @return 用户列表
     */
    @Override
    public List<User> getMatchUsers(long num, User loginUser) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "tags");
        queryWrapper.isNotNull("tags");
        List<User> userList = this.list(queryWrapper);

        // 获取当前的用户标签
        String tags = loginUser.getTags();
        Gson gson = new Gson();
        List<String> tagList = gson.fromJson(tags, new TypeToken<List<String>>() {}.getType());

        // 用户列表的下标 => 相似度
        List<Pair<User, Long>> list = new ArrayList<>();
        // 一次计算每个用户与当前用户的相似度
        for (User user : userList) {
            String userTags = user.getTags();
            // 标签判空或者为自己则跳过
            if (StringUtils.isBlank(userTags) || Objects.equals(user.getId(), loginUser.getId())){
                continue;
            }
            List<String> userTagList = gson.fromJson(userTags, new TypeToken<List<String>>() {}.getType());

            // 计算分数
            long distance = AlgorithmUtils.minDistance(userTagList, tagList);
            list.add(new Pair<>(user, distance));
        }

        // 按编辑距离由小到大排序
        List<Pair<User, Long>> topUserPairList = list.stream()
                .sorted((a,b) -> (int) (a.getValue() - b.getValue()))
                .limit(num)
                .toList();

        // 原本顺序的userId列表
        List<Integer> userIdList = topUserPairList.stream()
                .map(pair -> pair.getKey().getId())
                .toList();
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.in("id", userIdList);

        Map<Integer, List<User>> userIdUserListMap = this.list(userQueryWrapper).stream()
                .map(this::getSafetyUser)
                .collect(Collectors.groupingBy(User::getId));

        List<User> finalUserList = new ArrayList<>();
        for (Integer userId : userIdList) {
            finalUserList.add(userIdUserListMap.get(userId).get(0));
        }

        return finalUserList;
    }
}




